#include "stdafx.h"
#include "TreeBarrier.h"
#include "Globals.h"
#include <assert.h>
#include <iostream>

#include "Test.h"

using namespace std;
using namespace CMDR;

#define FAN_OUT 2
TreeBarrier *g_bar = new TreeBarrier(Globals::numberOfThreads,FAN_OUT);


void TreeBarrier::await()
{
	int me = tbThreadId.get() % Globals::numberOfThreads;
	int cell = me / _radix;
	Node* myLeaf = _leaf[cell];
    myLeaf->await();
}

void TreeBarrier::build(Node* parent, int depth)
{
	// are we at a leaf node?
	if (depth == 0)
	{
		_leaf[leaves++] = parent;
	} else 
	{
		for (int i = 0; i < _radix; i++)
		{
			Node* child = new Node(_radix, parent);
			build(child, depth - 1);
		}
	}
}

void TreeBarrier::initThreadLocals(int me)
{
	tbThreadId.set(me);
}

void TreeBarrier::Node::await()
{
	//when using a bool sense -for next line I get WARN: "forcing value to bool 'true' or 'false' (performance warning)" 
	int mySense = g_bar->threadSense.get();

    int position = count.getAndDecrement();

//	cout << "thread " << id << ", position: " << position << endl;
	
	if (position == 1)
	{    // I'm last
		if (parent != NULL) // root?
		{
			parent->await();
        }

		count.set(_fanout);     // reset counter
		global_sense = mySense;
		MB();// w/r mem barrier to make the change visible.
	}
	else
	{
		while (global_sense != mySense) 
		{
			//Thread::yield(); // use this when running on core2 due machine.
		};
	}

	int myNewSense = (mySense + 1) % 2;
	g_bar->threadSense.set(myNewSense);
}

void *initThread(void* arg)
{
	int id = *((int*) arg);
	g_bar->initThreadLocals(id);
	return(void*) NULL;
}

// MAIN
// first arg	= number of threads
// second arg	= max count to 
// third arg	= number of iterations 
// forth arg	= test type 0=counter, 1=localCounter 2=matrix
// fifth arg	= shared memory - 1 for shared mem, 0 for local mem.
// sixth arg	= barrier type. 0=SenseWithWait, 1=LinearSense, 2=BinaryStaticBarrier
int main(int argc,char* argv[])
{
	TestFactory factory;
	TestType testType = Counter; // default test type
	if (argc > 1)
	{
		Globals::numberOfThreads = atoi(argv[1]);
	}
	
	if (argc > 2)
	{
		Globals::countTo = atoi(argv[2]);
	}

	if (argc > 3)
	{
		Globals::numberOfIterations = atoi(argv[3]);
	}

	if (argc > 4)
	{
		int typeId = atoi(argv[4]);
		if (typeId == 0)
		{
			testType = Counter;
		} else if (typeId == 1)
		{
			testType = LocalCounter;
		} else if (typeId == 2)
		{
			testType = Matrix;
		} else {
			printf("Unsupported TestType id: %d. Exiting.\n", typeId);
			assert(false);
		}
	}

	
	g_bar = new TreeBarrier(Globals::numberOfThreads, FAN_OUT);
	printf("%s, Number of threads, %d, count to, %d, numOfIters:,%d\n", g_bar->getBarrierName(), Globals::numberOfThreads, Globals::countTo, Globals::numberOfIterations);
	fflush(stdout);

	Test* test = factory.createTest(g_bar, testType);

	test->performTest();
}
